在之前的學習中,我們探討了如何優化應用的性能和結構。隨著應用的增大,將所有功能集中在一個組件裡變得不夠靈活,因此,我們將進一步模組化應用功能,讓結構更加清晰,便於維護和擴展。
今天的目標是將功能分離到各自的頁面,並使用 React Router 構建靈活的頁面導航系統,這為我們後續的作品集網站開發打下基礎。我們還將新增一個 Home Page,讓用戶可以選擇進入不同的應用階段。
React Router 是 React 中的標準路由解決方案,能根據 URL 渲染對應的組件,實現單頁應用(SPA)的多頁效果。它能夠讓應用在不同頁面之間切換,而不必重載整個應用。
通過將邏輯分離到不同頁面,我們可以提升復用性和可維護性。例如,導航欄和頁腳可以統一管理,不需要在每個頁面中重複實現。嵌套路由允許我們在頂層路由中定義共用的佈局(如 Layout 組件),並通過 Outlet 組件渲染子路由的內容,確保共用組件(如導航欄和頁腳)在頁面切換時保持不變。。
今天的主要目標是進一步模組化應用並實現靈活的頁面導航。具體步驟如下:
App.jsx 中的邏輯與功能拆分到專屬的頁面模組中(如 BasicStage 和 Layout),使應用結構更加清晰,便於維護和擴展。以下是修改後的專案目錄結構,展示了今天所新增或修改的部分:
react-webpack-starter/
├── src/                    # React 原始碼
│   ├── components/         # React 組件
│   │   ├── Layout.module.scss   # 布局樣式
│   │   └── Layout.jsx           # 布局組件
│   ├── pages/              # 應用頁面
│   │   ├── BasicStage.jsx   # 基礎階段頁面
│   │   ├── IntermediateStage.jsx # 進階階段頁面
│   │   └── HomePage.jsx     # 應用入口頁面
│   ├── App.js              # 主應用組件
│   └── APP.module.scss      # 主應用樣式模組
這樣的目錄結構強調了應用的模組化設計,讓每個頁面和功能都有清晰的責任範疇,這有助於未來的維護和擴展。如果對模組化設計不熟悉,建議回顧 Day 6:基礎模組化設計優化 React 應用。
首先,我們需要開啟終端機,並輸入以下指令來安裝 react-router-dom,以啟用 React 的路由功能:
npm install react-router-dom
安裝完成後,我們需要將 BrowserRouter 引入應用中,包裹住我們的APP元件,這樣才能啟用路由功能。
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import '@/utils/i18n'; // 确保 i18n 初始化在应用渲染之前
import App from '@/App';
import { ThemeProvider } from '@/utils/ThemeContext';
import '@/styles/_global.scss' // 引入全局樣式
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <BrowserRouter>
        <ThemeProvider>
            <App />
        </ThemeProvider>
    </BrowserRouter>
);
HomePage 是用戶進入應用的第一個頁面,將為他們提供導航選項,讓他們選擇進入不同的階段頁面(基礎階段或進階階段)。
我們通過 Link 組件實現頁面導航,這是一種比傳統 <a> 標籤更高效的方法,因為它不會重新加載整個應用,只會更改 URL 和更新視圖。
// src/pages/HomePage.js
import React from 'react';
import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { log, logLevel } from '@/utils/log';
import { useTheme } from '@/utils/ThemeContext';
import * as styles from '@/pages/HomePage.module.scss'; // 使用模塊化的樣式
const HomePage = () => {
    // 使用 useTranslation 取得翻譯函數 t
    const { t, i18n } = useTranslation();
    const { isDarkMode } = useTheme();
    log(logLevel.DEBUG, 'HomePage rendered');
    const backgroundImage = isDarkMode ? require('@/assets/bg/night_image.png') : require('@/assets/bg/day_image.png');
    return (
        <div className={`${styles.homeContainer} ${i18n.language}`}
            style={{ backgroundImage: `url(${backgroundImage})` }}>
            <div className={styles.content}>
                <h1 className={styles.title}>{t('homepage.title')}</h1>
                <p className={styles.subtitle}>{t('homepage.subtitle')}</p>
                <div className={styles.buttonContainer}>
                    <Link to="/intro" className={
                        `${isDarkMode ? styles.dark_basic_btn : styles.light_basic_btn}`}>
                        {t('homepage.basic_stage')}
                    </Link>
                    <Link to="/portfolio" className={
                        `${isDarkMode ? styles.dark_adv_btn : styles.light_adv_btn}`
                    }>
                        {t('homepage.advanced_stage')}
                    </Link>
                </div>
            </div>
        </div >
    )
};
export default HomePage
HomePage 組件不僅負責展示簡單的內容,還提供了進入應用不同部分的導航入口,讓用戶能夠快速選擇進入不同的階段。
Layout 是應用的主框架,負責呈現應用的全局佈局,包括導航欄、頁腳以及其他全局功能。我們將原本位於 App.jsx 中的將 ThemeButton 和 LangButton 元件移動至 Layout 中,能夠確保無論使用者瀏覽到哪個頁面,這些按鈕始終保持在畫面中,提供一致的體驗。
以下是修改後的 Layout.jsx:
//src/components/Layout.jsx
import React, { useMemo } from 'react'
import { Outlet } from 'react-router-dom';
import ThemeButton from '@/components/Buttons/ThemeButton';
import LangButton from '@/components/Buttons/LangButton';
import { useTheme } from '@/utils/ThemeContext'
import Version from '@/components/Version';
import * as styles from '@/components/Layout.module.scss'
import { useTranslation } from 'react-i18next';
const Layout = () => {
    // 使用 useTranslation 取得翻譯函數 t
    const { t } = useTranslation();
    const { isDarkMode } = useTheme();
    // 使用 useMemo 緩存翻譯文本
    const buttonText = useMemo(() => {
        return isDarkMode ? t('dark_mode') : t('light_mode');
    }, [t, isDarkMode]);
    return (
        <div className={`${styles.appContainer}
                ${isDarkMode ? styles.darkMode : styles.lightMode}`}>
            <header>
                <nav>
                    <ThemeButton buttonText={buttonText} />
                    <LangButton />
                </nav>
            </header>
            <main className={styles.mainContent}>
                <Outlet /> {/* 這裡渲染頁面的主要內容 */}
            </main>
            <footer>
                <Version />
            </footer>
        </div>
    )
}
export default Layout
說明:
最後,我們需要在 App.jsx 中設置路由和嵌套路由:
import React from 'react';
import { Routes, Route } from 'react-router-dom';
import HomePage from '@/pages/HomePage';
import BasicStage from '@/pages/BasicStage';
import Layout from '@/components/Layout';
import IntermediateStage from '@/pages/IntermediateStage';
const App = () => {
    return (
        <Routes>
            <Route path="/" element={<Layout />}>
                <Route index element={<HomePage />} /> {/* 默認首頁路由 */}
                <Route path="intro" element={<BasicStage />} />
                <Route path='portfolio' element={<IntermediateStage />} />
                <Route path="*" element={<div>404 - Page Not Found</div>} /> {/* 通用 404 頁面 */}
            </Route>
        </Routes>
    );
};
export default App;
說明:
Routes 和 Route:定義了應用的導航結構。Layout 作為主佈局,HomePage 作為首頁,而 BasicStage 和 IntermediateStage 則作為應用的其他頁面。path="*":捕捉所有未定義的路徑,展示 404 頁面,這是應用處理錯誤路徑的關鍵步驟。今天,我們成功將應用功能模組化,並通過引入 React Router 建立靈活的頁面導航系統。同時,我們新增了 Home Page,提升了用戶體驗。我們還使用嵌套路由實現共用組件的持續顯示,並處理 404 錯誤頁面,進一步提升應用的靈活性與穩定性。

你是否曾經遇到過頁面導航混亂、無法正確處理錯誤路徑的問題?通過使用 React Router,你可以建立一個結構清晰且高度可維護的應用。
接下來的學習中,我們將進入 Intermediate Stage,並開始作品集網站的完整開發,探索頁面佈局、動畫效果等更高階的主題,最終實現一個現代、響應式的個人作品集網站。
此外,我們已將完整的代碼實作與更多練習題上傳至 GitHub,鼓勵大家前往查看,並回顧文章中的概念,挑戰更進階的優化練習。
👉 前往 GitHub 的 v0.11.0-react-router-navigation 查看完整程式碼
✨ 流光館Luma<∕> ✨ 期待與你繼續探索更多技術知識!